/*
 * Copyright (c) 2024 iSOFT INFRASTRUCTURE SOFTWARE CO., LTD.
 * easyAda is licensed under Mulan PubL v2.
 * You can use this software according to the terms and conditions of the Mulan PubL v2.
 * You may obtain a copy of Mulan PubL v2 at:
 *          http://license.coscl.org.cn/MulanPubL-2.0
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PubL v2 for more details.
 */

#ifndef ADDRSPACE_H
#define ADDRSPACE_H

#include <arch/scr.h>
#include <arch/pagetable.h>
#include <core/object.h>
#include <uapi/addrspace.h>
#include <uapi/bootinfo.h>

typedef struct image_info {
    region_t code;
    region_t rwdata;
} image_info_t;

typedef struct address_space {
    unsigned short asid;
    page_table_t  *pg;
    object_t       obj;
} addrspace_t;

extern image_info_t kernelinfo;
extern image_info_t rootserverinfo;

void          addrspace_recycle(addrspace_t *as);
addrspace_t  *kernel_addrspace(void);
addrspace_t  *user_addrspace(void);
void BOOTONLY prepare_addrspace(addrspace_t *as, const image_info_t *image, const region_t *ram, const region_t *dev,
                                unsigned int iskernel_flag);
void BOOTONLY prepare_dmaram(addrspace_t *as, const region_t *ram);
void          kernel_addrspace_init(void);
void          rootserver_addrspace_init(uint64_t *initsect_v, uint64_t *initsect_f);

void BOOTONLY kernel_addrspace_active(void);
void BOOTONLY user_addrspace_active(void);

void prepare_thread_addrspace(addrspace_t *des, addrspace_t *src);

unsigned short alloc_asid(void);
void           release_asid(unsigned short asid);

static inline int addrspace_map(addrspace_t *as, unsigned long pbase, unsigned long vbase, unsigned int size,
                                unsigned attr)
{
    int retval;
    spin_lock(&as->obj.lock);
    retval = page_table_map(as->pg, pbase, vbase, size, attr);
    spin_unlock(&as->obj.lock);
    if (retval == 0) {
        scr_invalidate_all_pe_tlb_asid_el1(as->asid);
    }
    return retval;
}

static inline int addrspace_unmap(addrspace_t *as, unsigned long vbase, unsigned int size)
{
    int retval;
    spin_lock(&as->obj.lock);
    retval = page_table_unmap(as->pg, vbase, size);

    if (retval == 0) {
        scr_invalidate_all_pe_tlb_asid_el1(as->asid);
    }

    spin_unlock(&as->obj.lock);
    return retval;
}

static inline int addrspace_get(addrspace_t *as)
{
    return object_get(&as->obj);
}

static inline void addrspace_put(addrspace_t *as)
{
    if (object_put(&as->obj, 0, 0) == 0) {
        addrspace_recycle(as);
    }
}

static inline void addrspace_init(addrspace_t *as)
{
    assert(as != NULL);
    if (as->pg == NULL) {
        as->pg = allocator_alloc_block(PAGE_SIZE);
    }

    assert(as->pg != NULL);
    (void)memset(as->pg, 0, sizeof(page_table_t));

    object_init(&as->obj);

    as->asid = alloc_asid();
}

#endif
